home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1999 July: Technology Seed / ADC Seed CD - July 1999.toast / Carbon SDK 1.0d10c3 / Sample Code / SimpleText / TextDrag.c < prev    next >
Encoding:
C/C++ Source or Header  |  1999-05-04  |  22.8 KB  |  859 lines  |  [TEXT/CWIE]

  1. /*
  2.     File:        TextDrag.c
  3.  
  4.     Contains:    Text document dragging support for SimpleText
  5.  
  6.     Version:    SimpleText 1.4 or later
  7.  
  8.     Written by:    Jim Murphy
  9.                 Tom Dowdy
  10.                 DAL = Dave Lyons
  11.  
  12.     Copyright:    © 1993-1998 by Apple Computer, Inc., all rights reserved.
  13.  
  14.     File Ownership:
  15.  
  16.         DRI:                Tom Dowdy
  17.  
  18.         Other Contact:        Jim Negrette
  19.  
  20.         Technology:            Macinotsh Graphics Group
  21.         
  22.         Writers:
  23.  
  24.         (dmp)    Dave Polaschek
  25.         (ted)    Tom Dowdy
  26.         (TD)    Tom Dowdy
  27.  
  28.     Change History (most recent first):
  29.  
  30.     $Log: TextDrag.c,v $
  31.     Revision 1.5  1999/02/19 22:11:47  danp
  32.     Minor changes for opaque regions.
  33.  
  34.     Revision 1.4  1999/02/16 00:41:40  christ
  35.     Integrate document proxy icon support
  36.     
  37.     Revision 1.3  1999/01/08 04:42:01  christ
  38.     Text dragging support is back.
  39.     
  40.     Revision 1.2  1998/10/12 18:59:04  danp
  41.     Modifications to allow cross-compiling and for CarbonLib support. Removed lots of GX stuff
  42.     
  43.     Revision 1.1.1.1  1998/03/18 22:56:11  ivory
  44.     Initial checkin of SimpleText.
  45.     
  46.         
  47.         3     1/21/98 3:00 PM Tom Dowdy
  48.         Dont pass bounds in an unlocked handle.
  49.         
  50.         2     7/29/97 2:05 PM Tom Dowdy
  51.         Removed all of the old and boring refs
  52.         
  53.         1     7/28/97 11:24 AM Duane Byram
  54.         first added to Source Safe project
  55.  
  56.         <10>     5/14/97    ted        More drag clipping bug fixes
  57.          <9>     5/14/97    ted        More drag clipping bug fixes
  58.          <8>      5/6/97    ted        [1609760]  Clipping flavors must be the same
  59.          <7>     4/21/97    ted        [1609760]  Adding clipping name for text drag out
  60.          <6>    10/11/96    ted        [1391143]  Fixing line height when dragging
  61.          <5>    10/10/96    ted        changing implementforwarddelete to TE6 version
  62.          <4>      9/9/96    dmp        staticfy local functions to eliminate warnings in MWC.
  63.          <3>      2/8/96    ted        D11 Interfaces changes bite me.
  64.          <2>     9/13/95    TD        removing double declared variable.
  65.          <1>     8/21/95    TD        First checked in.
  66.          <5>    12/21/94    DAL        Fixed header.
  67.          <4>    12/21/94    DAL        Fixed the wasActive check in TextDragReceive (Radar #1206471).
  68.                                     This was leaving old selection-hiliting outlines lying around
  69.                                     after dragging in text from another application to what would be
  70.                                     the frontmost SimpleText window. Also, we now scroll the source
  71.                                     window when you drag text to the top or bottom edge (Radar
  72.                                     #1207642).
  73.  
  74. */
  75.  
  76.  
  77. #include "MacIncludes.h"
  78.  
  79. #include "TextFile.h"
  80.  
  81. #pragma segment Text
  82.  
  83.  
  84. // --------------------------------------------------------------------------------------------------------------
  85. // FORWARD EXTERNAL DECLARES
  86. // --------------------------------------------------------------------------------------------------------------
  87.  
  88. extern Boolean IsOnlyThisFlavor(DragReference theDragRef, FlavorType theType);
  89. extern Boolean IsDropInFinderTrash(AEDesc *dropLocation);
  90. extern OSErr SaveCurrentUndoState(WindowDataPtr pData, short newCommandID);
  91.  
  92. // --------------------------------------------------------------------------------------------------------------
  93. // INTERNAL DEFINES
  94. // --------------------------------------------------------------------------------------------------------------
  95.  
  96. // Dragging private globals
  97.  
  98. extern Boolean    gCanAccept;
  99. extern Boolean    gTE6Version;
  100.  
  101. // --------------------------------------------------------------------------------------------------------------
  102. // GLOBALS USED ONLY BY THESE ROUTINES
  103. // --------------------------------------------------------------------------------------------------------------
  104.  
  105. static Boolean    gCaretShow;
  106. static long        gCaretTime;
  107. static short    gCaretOffset, gLastOffset, gInsertPosition;
  108. static Boolean    gCursorInContent;
  109.  
  110. static unsigned long gAutoScrollTicks;
  111.  
  112. // --------------------------------------------------------------------------------------------------------------
  113. // INTERNAL ROUTINES
  114. // --------------------------------------------------------------------------------------------------------------
  115.  
  116. extern void AdjustTE(WindowDataPtr pData, Boolean doScroll);
  117.  
  118. // --------------------------------------------------------------------------------------------------------------
  119. //
  120. // GetSelectionSize -
  121. //
  122.  
  123. static short GetSelectionSize(TEHandle hTE)
  124. {
  125.  
  126.     return((**(hTE)).selEnd - (**(hTE)).selStart);
  127.  
  128. } // GetSelectionSize
  129.  
  130.  
  131. // --------------------------------------------------------------------------------------------------------------
  132. //
  133. // GetSelectedTextPtr
  134. //
  135.  
  136. static Ptr GetSelectedTextPtr(TEHandle hTE)
  137. {
  138.  
  139.     return((*(**(hTE)).hText) + (**(hTE)).selStart);
  140.  
  141. } // GetSelectedTextPtr
  142.  
  143.  
  144. // --------------------------------------------------------------------------------------------------------------
  145. //
  146. //    TEIsFrontOfLine - Given a text offset and a TextEdit handle, returns true if the given
  147. //                      offset is at the beginning of a line start.
  148. //
  149.  
  150. static short TEIsFrontOfLine(short textOffset, TEHandle hTE)
  151. {
  152.  
  153.     short theLine = 0;
  154.  
  155.     if ((**hTE).teLength == 0)
  156.         return true;
  157.  
  158.     if (textOffset >= (**hTE).teLength)
  159.         return( (*((**hTE).hText))[(**hTE).teLength - 1] == 0x0D );
  160.  
  161.     while ((**hTE).lineStarts[theLine] < textOffset)
  162.         theLine++;
  163.  
  164.     return( (**hTE).lineStarts[theLine] == textOffset );
  165.  
  166. } // TEIsFrontOfLine
  167.  
  168.  
  169. // --------------------------------------------------------------------------------------------------------------
  170. //
  171. //    TEGetLine - Given an offset and a TextEdit handle, returns the line number that contains the offset.
  172. //
  173.  
  174. static short TEGetLine(short textOffset, TEHandle hTE)
  175. {    
  176.  
  177.     short theLine = 0;
  178.  
  179.     if (textOffset > (**hTE).teLength)
  180.         return((**hTE).nLines);
  181.     else
  182.         {
  183.         while ((**hTE).lineStarts[theLine] <= textOffset)
  184.             ++theLine;
  185.         }
  186.         
  187.     return theLine;
  188.  
  189. } // TEGetLine
  190.  
  191.  
  192. // --------------------------------------------------------------------------------------------------------------
  193. //
  194. //    DrawCaret - Draws a caret in a TextEdit field at the given offset by inverting the image of the
  195. //                caret onto the screen. DrawCaret expects the port to be set to the port that the
  196. //                TextEdit record is in.
  197. //
  198.  
  199. static void DrawCaret(short textOffset, TEHandle hTE)
  200. {    
  201.  
  202.     Point theLoc;
  203.     short lineHeight, theLine;
  204.  
  205.     //
  206.     //    Get the coordinates and the line of the offset to draw the caret.
  207.     //
  208.  
  209.     theLoc  = TEGetPoint(textOffset, hTE);
  210.     theLine = TEGetLine(textOffset, hTE);
  211.     
  212.     if (!gTE6Version)
  213.         {
  214.         // %%% Most heinously bogus - 21-Dec-93 JM3
  215.         //
  216.         //    For some reason, TextEdit dosen't return the proper coordinates
  217.         //    of the last offset in the field if the last character in the record
  218.         //    is a carriage return. TEGetPoint returns a point that is one line
  219.         //    higher than expected. The following code fixes this problem.
  220.     
  221.         // It has also been fixed for TE6
  222.         
  223.         if ((textOffset == (**hTE).teLength) && (*((**hTE).hText))[(**hTE).teLength - 1] == 0x0D)
  224.             theLoc.v += TEGetHeight(theLine, theLine, hTE);
  225.         }
  226.     
  227.     //
  228.     //    Always invert the caret when drawing.
  229.     //
  230.  
  231.     PenMode(patXor);
  232.  
  233.     //
  234.     //    Get the height of the line that the offset points to.
  235.     //
  236.  
  237.     lineHeight = TEGetHeight(theLine, theLine, hTE);
  238.  
  239.     //
  240.     //    Draw the appropriate caret image.
  241.     //
  242.  
  243.     MoveTo(theLoc.h - 1, theLoc.v - 1);
  244.     Line(0, 1 - lineHeight);
  245.  
  246.     PenNormal();
  247.  
  248. } // DrawCaret
  249.  
  250.  
  251. // --------------------------------------------------------------------------------------------------------------
  252. //
  253. //    HitTest - Given a point in global coordinates, HitTest returns an offset into the text if the
  254. //              point is inside the given TERecord. If the point is not in the text, HitTest returns
  255. //              -1.
  256. //
  257.  
  258. static short HitTest(Point theLoc, TEHandle hTE)
  259. {    
  260.  
  261.     WindowPtr    theWindow;
  262.     short        textOffset = -1;
  263.  
  264.     if (FindWindow(theLoc, &theWindow) == inContent)
  265.         {
  266.         SetPort((GrafPtr) GetWindowPort(theWindow));
  267.         GlobalToLocal(&theLoc);
  268.  
  269.         if (PtInRect(theLoc, &((** hTE).viewRect)) && PtInRect(theLoc, &((** hTE).viewRect)))
  270.             {
  271.             textOffset = TEGetOffset(theLoc, hTE);
  272.  
  273.             if ((TEIsFrontOfLine(textOffset, hTE)) && (textOffset) &&
  274.                 ((*((** hTE).hText))[textOffset - 1] != 0x0D) &&
  275.                 (TEGetPoint(textOffset - 1, hTE).h < theLoc.h))
  276.                 {
  277.                 --textOffset;
  278.                 }
  279.             }
  280.         }
  281.  
  282.     return textOffset;
  283.  
  284. } // HitTest
  285.  
  286.  
  287. // --------------------------------------------------------------------------------------------------------------
  288. //
  289. // GetCharAtOffset - Given a text offset and a TEHandle, returns the character located at that offset in
  290. //                     the TERecord.
  291. //
  292.  
  293. static char GetCharAtOffset(short offset, TEHandle hTE)
  294. {
  295.  
  296.     if (offset < 0)
  297.         return 0x0D;
  298.  
  299.     return(((char *) *((**hTE).hText))[offset]);
  300.  
  301. } // GetCharAtOffset
  302.  
  303.  
  304. // --------------------------------------------------------------------------------------------------------------
  305. //
  306. // WhiteSpace - Determines if the input character is white space.
  307. //
  308.  
  309. static Boolean WhiteSpace(char theChar)
  310. {
  311.  
  312.     return((theChar == ' ') || (theChar == 0x0D));
  313.  
  314. } // WhiteSpace
  315.  
  316.  
  317. // --------------------------------------------------------------------------------------------------------------
  318. //
  319. // WhiteSpaceAtOffset - Given a text offset into a TERecord, determines if the character at that location is
  320. //                        whitespace.
  321. //
  322.  
  323. static Boolean WhiteSpaceAtOffset(short offset, TEHandle hTE)
  324. {
  325.  
  326.     char theChar;
  327.  
  328.     if ((offset < 0) || (offset > (**hTE).teLength - 1))
  329.         return true;
  330.  
  331.     theChar = ((char *) *((**hTE).hText))[offset];
  332.     return(WhiteSpace(theChar));
  333.  
  334. } // WhiteSpaceAtOffset
  335.  
  336.  
  337. // --------------------------------------------------------------------------------------------------------------
  338. //
  339. // InsertTextAtOffset -
  340. //
  341.  
  342. static short InsertTextAtOffset(short textOffset, char *theBuf, long textSize, StScrpHandle styleHand, TEHandle hTE)
  343. {
  344.     short    charactersAdded = 0;
  345.     
  346.     if (textSize == 0)
  347.         return charactersAdded;
  348.  
  349.     //    If we're inserting at the end of a word and the selection does not begin with
  350.     //    a space, insert a space before the insertion.
  351.  
  352.     if (!WhiteSpaceAtOffset(textOffset - 1, hTE) &&
  353.          WhiteSpaceAtOffset(textOffset, hTE) &&
  354.         !WhiteSpace(theBuf[0]))
  355.         {
  356.  
  357.         TESetSelect(textOffset, textOffset, hTE);
  358.         TEKey(' ', hTE);
  359.         ++textOffset;
  360.         ++charactersAdded;
  361.         }
  362.  
  363.     //    If we're inserting at the beginning of a word and the selection does not end
  364.     //    with a space, insert a space after the insertion.
  365.  
  366.     if ( WhiteSpaceAtOffset(textOffset - 1, hTE) &&
  367.         !WhiteSpaceAtOffset(textOffset, hTE) &&
  368.         !WhiteSpace(theBuf[textSize - 1]))
  369.         {
  370.  
  371.         TESetSelect(textOffset, textOffset, hTE);
  372.         TEKey(' ', hTE);
  373.         ++charactersAdded;
  374.         }
  375.  
  376.     // Before we insert this text, make sure we set the selection range to a single character.
  377.     // This assures that we won't overwrite the text in the previous selection.
  378.     
  379.     TESetSelect(textOffset, textOffset, hTE);
  380.     TEStyleInsert(theBuf, textSize, styleHand, hTE);
  381.  
  382.     return charactersAdded;
  383.     
  384. } // InsertTextAtOffset
  385.  
  386. // --------------------------------------------------------------------------------------------------------------
  387. // OOP INTERFACE ROUTINES
  388. // --------------------------------------------------------------------------------------------------------------
  389.  
  390. OSErr TextDragTracking(WindowPtr pWindow, void *pData, DragReference theDragRef, short message)
  391. {
  392. #pragma unused(pWindow)
  393.  
  394.     unsigned long    attributes;
  395.     RgnHandle        hilightRgn;
  396.     Point            localMouseLoc, dragMouseLoc;
  397.     short            textOffset;
  398.     long            theTime = TickCount();
  399.     WindowDataPtr    theData = (WindowDataPtr) pData;
  400.     Rect             pRect; // (DP) Needed because of Opacity Issues
  401.  
  402.  
  403.     GetDragAttributes(theDragRef, &attributes);
  404.  
  405.     switch(message)
  406.         {
  407.         case kDragTrackingEnterWindow:
  408.  
  409.             gCanAccept = IsOnlyThisFlavor(theDragRef, 'TEXT');
  410.  
  411.             gCaretTime   = theTime;
  412.             gCaretOffset = gLastOffset = -1;
  413.             gCaretShow   = true;
  414.  
  415.             gCursorInContent = false;
  416.             gAutoScrollTicks = 0;
  417.  
  418.             break;        
  419.  
  420.         case kDragTrackingInWindow:
  421.  
  422.             if (gCanAccept)
  423.                 {            
  424.                 GetDragMouse(theDragRef, &dragMouseLoc, 0L);
  425.                 localMouseLoc = dragMouseLoc;
  426.                 GlobalToLocal(&localMouseLoc);
  427.  
  428.                 if (attributes & kDragInsideSenderWindow)
  429.                     {
  430.                     short deltaV = 0;
  431.  
  432.                     if ((localMouseLoc.v < 16) && (localMouseLoc.v > 0))
  433.                         deltaV = theData->vScrollAmount;
  434.  
  435.                     if (localMouseLoc.v > GetPortBounds(GetWindowPort(pWindow), &pRect)->bottom - 16)
  436.                         deltaV -= theData->vScrollAmount;
  437.  
  438.                     if (deltaV == 0)
  439.                         {
  440.                         gAutoScrollTicks = 0;
  441.                         }
  442.                     else
  443.                         {
  444.                         if (gAutoScrollTicks == 0)
  445.                             {
  446.                             gAutoScrollTicks = theTime;
  447.                             }
  448.                         else
  449.                             {
  450.                             if (theTime - gAutoScrollTicks > 10)    // 10 ticks to start is what the H.I. doc says
  451.                                 {
  452.                                 // remove the drag-destination caret if it's showing
  453.                                 if (gCaretOffset != -1)
  454.                                     {
  455.                                     DrawCaret(gCaretOffset, ((TextDataPtr) pData)->hTE);
  456.                                     gCaretOffset = -1;
  457.                                     }
  458.     
  459.                                 SetControlAndClipAmount(theData->vScroll, &deltaV);
  460.                                 if (deltaV != 0)
  461.                                     {
  462.                                     DragPreScroll(theDragRef, 0, deltaV);
  463.                                     DoScrollContent(pWindow, theData, 0, deltaV);
  464.                                     DragPostScroll(theDragRef);
  465.                                     }
  466.                                 
  467.                                 gAutoScrollTicks = theTime - 7;    // let's wait 3 more ticks until next jump
  468.                                 }
  469.                             }
  470.                         }
  471.                     }
  472.  
  473.                 if (attributes & kDragHasLeftSenderWindow)
  474.                     {
  475.                     if (PtInRect(localMouseLoc, &(theData->contentRect)))
  476.                         {
  477.                         if (!gCursorInContent)
  478.                             {
  479.                             hilightRgn = NewRgn();
  480.                             RectRgn(hilightRgn, &theData->contentRect);
  481.                             ShowDragHilite(theDragRef, hilightRgn, true);                    
  482.                             DisposeRgn(hilightRgn);
  483.                             }
  484.                         gCursorInContent = true;
  485.                         }
  486.                     else
  487.                         {
  488.                         if (gCursorInContent)
  489.                             {
  490.                             HideDragHilite(theDragRef);
  491.                             gCursorInContent = false;
  492.                             }
  493.                         }
  494.                     }
  495.                 }
  496.  
  497.             textOffset = HitTest(dragMouseLoc, ((TextDataPtr)pData)->hTE);
  498.  
  499.             //    If this application is the sender, do not allow tracking through
  500.             //    the selection in the window that sourced the drag.
  501.  
  502.             if (attributes & kDragInsideSenderWindow)
  503.                 {
  504.                 if ((textOffset >= (**((TextDataPtr)pData)->hTE).selStart) &&
  505.                     (textOffset <= (**((TextDataPtr)pData)->hTE).selEnd))
  506.                     {
  507.                         textOffset = -1;
  508.                     }
  509.                 }
  510.  
  511.             gInsertPosition = textOffset;
  512.  
  513.             //    Reset the flashing counter if the offset has moved. This makes the
  514.             //    caret blink only after the caret has stopped moving long enough.
  515.  
  516.             if (textOffset != gLastOffset)
  517.                 {
  518.                 gCaretTime = theTime;
  519.                 gCaretShow = true;
  520.                 }
  521.             
  522.             gLastOffset = textOffset;
  523.  
  524.             //    Flash the caret, blinky-blinky-blinky.
  525.  
  526.             if (theTime - gCaretTime > GetCaretTime())
  527.                 {
  528.                 gCaretShow = !gCaretShow;
  529.                 gCaretTime = theTime;
  530.                 }
  531.             
  532.             if (!gCaretShow)
  533.                 textOffset = -1;
  534.  
  535.             //    If the caret offset has changed, move the caret on the screen.
  536.  
  537.             if (textOffset != gCaretOffset)
  538.                 {
  539.                 if (gCaretOffset != -1)
  540.                     DrawCaret(gCaretOffset, ((TextDataPtr)pData)->hTE);
  541.  
  542.                 if (textOffset != -1)
  543.                     DrawCaret(textOffset, ((TextDataPtr)pData)->hTE);
  544.                 }
  545.  
  546.             gCaretOffset = textOffset;
  547.             break;
  548.  
  549.         case kDragTrackingLeaveWindow:
  550.     
  551.             //    If the caret is on the screen, remove it.
  552.  
  553.             if (gCaretOffset != -1)
  554.                 {
  555.                 DrawCaret(gCaretOffset, ((TextDataPtr)pData)->hTE);
  556.                 gCaretOffset = -1;
  557.                 }
  558.  
  559.             // Remove the window hilighting, if any.
  560.     
  561.             if ((gCursorInContent) && (attributes & kDragHasLeftSenderWindow))
  562.                 HideDragHilite(theDragRef);
  563.  
  564.             break;
  565.             
  566.           } // switch (message)
  567.     
  568.     return noErr;
  569.  
  570. } // TextDragTracking
  571.  
  572.  
  573. // --------------------------------------------------------------------------------------------------------------
  574.  
  575. OSErr TextDragReceive(WindowPtr pWindow, void *pData, DragReference theDragRef)
  576. {
  577.  
  578.     OSErr            error;
  579.     unsigned short    items, index;
  580.     DragAttributes    attributes;
  581.     ItemReference    theItem;
  582.     Ptr                textData;
  583.     StScrpHandle    styleHand;
  584.     Size            textSize, styleSize, totalTextSize;
  585.     short            mouseDownModifiers, mouseUpModifiers, moveText, selStart, selEnd;
  586.     long            totalTextStart;
  587.     long            additionalChars;
  588.     TEHandle        hTE;
  589.     Boolean            wasActive;
  590.     
  591.     if ((!gCanAccept)  || (gInsertPosition == -1))
  592.         return dragNotAcceptedErr;
  593.  
  594.     hTE = ((TextDataPtr) pData)->hTE;
  595.  
  596.     // We're going to try our best to insert some text, so first save off the beginning of where
  597.     // we'll do it.
  598.  
  599.     totalTextStart = gInsertPosition;
  600.     totalTextSize = 0L;
  601.  
  602.     // draw in this window, and activate the text editing record so that selections
  603.     // happen properly
  604.  
  605.     SetPort((GrafPtr) GetWindowPort(pWindow));
  606.  
  607.     wasActive = (*hTE)->active != 0;    // can't test window == FrontWindow (might not be front app)
  608.     if (!wasActive)
  609.         TEActivate(hTE);
  610.  
  611.     GetDragAttributes(theDragRef, &attributes);
  612.     GetDragModifiers(theDragRef, 0L, &mouseDownModifiers, &mouseUpModifiers);
  613.  
  614.     moveText = (attributes & kDragInsideSenderWindow) &&
  615.                (!((mouseDownModifiers & optionKey) | (mouseUpModifiers & optionKey)));
  616.  
  617.     //    Loop through all of the drag items contained in this drag and snag all of the 'TEXT'.
  618.  
  619.     CountDragItems(theDragRef, &items);
  620.     
  621.     for (index = 1; index <= items; index++)
  622.         {
  623.         //    Get the item's reference number, so we can refer to it.
  624.  
  625.         GetDragItemReferenceNumber(theDragRef, index, &theItem);
  626.  
  627.         //    Try to get the size for a 'TEXT' flavor. If this returns noErr,
  628.         //    then we know that a 'TEXT' flavor exists in the item.
  629.         
  630.         error = GetFlavorDataSize(theDragRef, theItem, 'TEXT', &textSize);
  631.  
  632.         if (error == noErr)
  633.             {        
  634.             // If the current length, plus the drag data would make the document too large, say so.
  635.  
  636.             if (((*hTE)->teLength + textSize) > kMaxLength)
  637.                 return eDocumentTooLarge;
  638.  
  639.             textData = NewPtr(textSize);
  640.     
  641.             // If we couldn't get a chunk of memory for the text, bail.
  642.  
  643.             if(textData == 0L)
  644.                 return memFullErr;
  645.  
  646.             GetFlavorData(theDragRef, theItem, 'TEXT', textData, &textSize, 0);
  647.  
  648.             // Let's see if there is an optional 'styl' flavor.
  649.  
  650.             styleHand = 0L;
  651.             
  652.             error = GetFlavorDataSize(theDragRef, theItem, 'styl', &styleSize);
  653.  
  654.             // If there was no 'styl' data, or it somehow was zero in length, don't
  655.             // attempt to insert it along with the text, 'cause we'd fail miserably.
  656.  
  657.             if ((error == noErr) && (styleSize != 0))
  658.                 {
  659.                 styleHand = (StScrpHandle) NewHandle(styleSize);
  660.             
  661.                 // If we couldn't get a chunk of memory for the styles, also bail.
  662.  
  663.                 if (styleHand == 0L)
  664.                     {    
  665.                     DisposePtr(textData);
  666.                     return memFullErr;
  667.                     }
  668.  
  669.                 HLock((Handle) styleHand);
  670.                 GetFlavorData(theDragRef, theItem, 'styl', *styleHand, &styleSize, 0L);
  671.                 HUnlock((Handle) styleHand);            
  672.                 }
  673.  
  674.             //    If the caret or highlighting is on the screen, remove it/them.
  675.     
  676.             if (gCaretOffset != -1)
  677.                 {
  678.                 DrawCaret(gCaretOffset, hTE);
  679.                 gCaretOffset = -1;
  680.                 }
  681.             
  682.             if (attributes & kDragHasLeftSenderWindow)
  683.                 HideDragHilite(theDragRef);
  684.  
  685.             // save away any changes, so that we can undo them
  686.             SaveCurrentUndoState(pData, cTypingCommand);
  687.             
  688.             // If this window is also the sender, delete the source selection text if the
  689.             // option key is not being held down.
  690.  
  691.             if (moveText)
  692.                 {
  693.                 selStart = (**hTE).selStart;
  694.                 selEnd   = (**hTE).selEnd;
  695.                 
  696.                 if ( WhiteSpaceAtOffset(selStart - 1, hTE) &&
  697.                     !WhiteSpaceAtOffset(selStart,     hTE) &&
  698.                     !WhiteSpaceAtOffset(selEnd - 1,   hTE) &&
  699.                      WhiteSpaceAtOffset(selEnd,       hTE))
  700.                     {
  701.                      
  702.                      if (GetCharAtOffset(selEnd, hTE) == ' ')
  703.                         (**hTE).selEnd++;
  704.                     }
  705.                 
  706.                 if (gInsertPosition > selStart)
  707.                     {
  708.                     selEnd = (**hTE).selEnd;
  709.                     gInsertPosition -= (selEnd - selStart);
  710.                     totalTextStart -= (selEnd - selStart);
  711.                     }
  712.                 
  713.                 TEDelete(hTE);    
  714.                 }
  715.  
  716.             // We can finally insert the text and style data into our record.
  717.  
  718.             additionalChars = InsertTextAtOffset(gInsertPosition, textData, textSize, styleHand, hTE);
  719.             
  720.             // In case we're inserting multiple chunks of text, we need to update the location of where we
  721.             // need to insert the next block.
  722.  
  723.             gInsertPosition += textSize + additionalChars;
  724.             totalTextSize += textSize + additionalChars;
  725.             
  726.             DisposePtr(textData);
  727.             if (styleHand)
  728.                 DisposeHandle((Handle) styleHand);
  729.             }
  730.         }
  731.  
  732.     // Select everything we've just inserted.
  733.  
  734.     TESetSelect(totalTextStart, totalTextStart + totalTextSize, hTE);
  735.  
  736.     AdjustTE(pData, false);
  737.     AdjustScrollBars(pWindow, false, false, nil);
  738.     
  739.     SetDocumentContentChanged( (WindowDataPtr) pData, true );
  740.  
  741.     // if we had to activate the edit record, deactivate it after we are all done
  742.     if (!wasActive)
  743.         TEDeactivate(hTE);
  744.  
  745.     return noErr;
  746.  
  747. } // TextDragReceive
  748.  
  749.  
  750. // --------------------------------------------------------------------------------------------------------------
  751.  
  752. Boolean DragText(WindowPtr pWindow, void *pData, EventRecord *pEvent, RgnHandle hilightRgn)
  753. {
  754.  
  755.     Point            theLoc = {0,0};
  756.     RgnHandle        dragRegion, tempRegion;
  757.     DragReference    theDragRef;
  758.     StScrpHandle    theStyleHand = 0L;
  759.     OSErr            error;
  760.     AEDesc            dropLocation;
  761.     DragAttributes    attributes;
  762.     short            mouseDownModifiers, mouseUpModifiers, copyText;
  763.  
  764.     //    Copy the hilight region into dragRegion and offset it into global coordinates.
  765.  
  766.     CopyRgn(hilightRgn, dragRegion = NewRgn());
  767.     LocalToGlobal(&theLoc);
  768.     OffsetRgn(dragRegion, theLoc.h, theLoc.v);
  769.  
  770.     //    Wait for the mouse to move or the mouse button to be released. If the mouse button was
  771.     //    released before the mouse moves, return false. Returing false from DragText means that
  772.     //    a drag operation did not occur.
  773.  
  774.     if (!WaitMouseMoved(pEvent->where))
  775.         return false;
  776.  
  777.     NewDrag(&theDragRef);
  778.  
  779.     // add the flavor for the window title, errors can be ignored as this
  780.     // is a cosmetic addition
  781.     {
  782.     enum
  783.         {
  784.         kFlavorTypeClippingName = 'clnm'
  785.         };
  786.     Str255    windowTitle;
  787.     
  788.     GetWTitle(pWindow, windowTitle);
  789.     (void)     AddDragItemFlavor(theDragRef, 1, kFlavorTypeClippingName, &windowTitle, windowTitle[0]+1, flavorNotSaved);
  790.     }
  791.  
  792.     AddDragItemFlavor(theDragRef, 1, 'TEXT', GetSelectedTextPtr(((TextDataPtr)pData)->hTE), GetSelectionSize(((TextDataPtr)pData)->hTE), 0);
  793.  
  794.     theStyleHand = TEGetStyleScrapHandle(((TextDataPtr)pData)->hTE);
  795.  
  796.     // Just be a little paranoid and see if we did get a handle.
  797.  
  798.     if (theStyleHand)
  799.         {
  800.         HLock((Handle) theStyleHand);
  801.         AddDragItemFlavor(theDragRef, 1, 'styl', (Ptr) *theStyleHand, GetHandleSize((Handle) theStyleHand), 0);
  802.         DisposeHandle((Handle) theStyleHand);
  803.         }
  804.  
  805.     //    Set the item's bounding rectangle in global coordinates.
  806.         {
  807.         Rect    dragBounds;
  808.         
  809.         SetDragItemBounds(theDragRef, 1, GetRegionBounds( dragRegion, &dragBounds ));
  810.         }
  811.         
  812.     //    Prepare the drag region.
  813.  
  814.     tempRegion = NewRgn();
  815.     CopyRgn(dragRegion, tempRegion);
  816.     InsetRgn(tempRegion, 1, 1);
  817.     DiffRgn(dragRegion, tempRegion, dragRegion);
  818.     DisposeRgn(tempRegion);
  819.  
  820.     //    Drag the text. TrackDrag will return userCanceledErr if the drop whooshed back for any reason.
  821.  
  822.     error = TrackDrag(theDragRef, pEvent, dragRegion);
  823.     
  824.     if ((error != noErr) && (error != userCanceledErr))
  825.         return true;
  826.  
  827.     //    Check to see if the drop occurred in the Finder's Trash. If the drop occurred
  828.     //    in the Finder's Trash and a copy operation wasn't specified, delete the
  829.     //    source selection. Note that we can continute to get the attributes, drop location
  830.     //    modifiers, etc. of the drag until we dispose of it using DisposeDrag.
  831.  
  832.     GetDragAttributes(theDragRef, &attributes);
  833.     if (!(attributes & kDragInsideSenderApplication))
  834.         {
  835.         GetDropLocation(theDragRef, &dropLocation);
  836.  
  837.         GetDragModifiers(theDragRef, 0L, &mouseDownModifiers, &mouseUpModifiers);
  838.         copyText = (mouseDownModifiers | mouseUpModifiers) & optionKey;
  839.  
  840.         if ((!copyText) && (IsDropInFinderTrash(&dropLocation)))
  841.             {
  842.             TEDelete(((TextDataPtr)pData)->hTE);
  843.             AdjustTE(pData, false);
  844.             AdjustScrollBars(pWindow, false, false, nil);
  845.             SetDocumentContentChanged( (WindowDataPtr) pData, true );
  846.             }
  847.  
  848.         AEDisposeDesc(&dropLocation);
  849.         }
  850.  
  851.     // Dispose of this drag, 'cause we're done.
  852.  
  853.     DisposeDrag(theDragRef);
  854.  
  855.     DisposeRgn(dragRegion);
  856.  
  857.     return true;
  858.  
  859. } // DragText